using System;
using System.IO;
using System.Collections.Generic;

namespace cssp
{
	public class CssWriter
	{
		public delegate Stream GetStreamDelegate(string filename);
		public GetStreamDelegate GetStream;

		Unit mUnit;

		public void Write(Unit unit)
		{
			if (unit == null)
				throw new ArgumentNullException("unit");

			if (unit.HasErrors)
				throw new InvalidOperationException("Unit has errors, and will not be written.");

			if (GetStream == null)
				throw new InvalidOperationException("GetStream must be set before calling Write.");

			mUnit = unit;
			if (unit.Targets.Keys.Count == 0)
			{
				string filename = GetFilename(unit.Filename, String.Empty);
				WriteCss(String.Empty, GetStream(filename));
			}
			else
			{
				foreach (string target in unit.Targets.Keys)
				{
					if (target[0] == '!')
						continue;
					string filename = GetFilename(unit.Filename, target);
					WriteCss(target, GetStream(filename));
				}
			}
			mUnit = null;
		}

		string GetFilename(string filename, string target)
		{
			filename = Path.GetFileNameWithoutExtension(filename);
			if (target.Length == 0)
				return filename + ".css";
			else
				return filename + "-" + target + ".css";
		}

		void WriteCss(string target, Stream stream)
		{
			TextReader tr = new StringReader(mUnit.Text);
			int lineNum = 0;
			CodeBlock currTarget = null;

			using (StreamWriter sw = new StreamWriter(stream))
			{
				string line = tr.ReadLine();
				while (line != null)
				{
					lineNum++;
					CodeBlock block = GetCodeBlock(lineNum);
					if (block == null)
					{
						if (currTarget == null
							|| (currTarget.Name == target && lineNum < currTarget.EndLineNum)
							|| ((currTarget.Name[0] == '!' && currTarget.Name.Substring(1) != target) && lineNum < currTarget.EndLineNum))
							sw.WriteLine(line);
						else if (lineNum >= currTarget.EndLineNum)
							currTarget = null;
					}
					else
					{
						if (block.Type == BlockType.Target)
							currTarget = block;
					}

					line = tr.ReadLine();
				}
			}
		}

		CodeBlock GetCodeBlock(int lineNum)
		{
			foreach (CodeBlock cb in mUnit.Imports)
				if (cb.StartLineNum == lineNum)
					return cb;

			foreach (CodeBlock cb in mUnit.Symbols)
				if (cb.StartLineNum == lineNum)
					return cb;

			foreach (List<CodeBlock> blockList in mUnit.Targets.Values)
				foreach (CodeBlock cb in blockList)
					if (cb.StartLineNum == lineNum)
						return cb;

			return null;
		}
	}
}
